#pragma once

#include "headers.hpp"

namespace alpha { namespace http {
	
namespace detail {

template <typename AsyncReadStream, typename Buffer, typename HTTP_Request, typename ReadHandler>
class async_read_request_handler
{
public:
	async_read_request_handler(AsyncReadStream &stream, Buffer &streambuf, HTTP_Request &request, ReadHandler handler)
		: stream_(stream), streambuf_(streambuf), request_(request), handler_(handler)
	{}
	void operator()(const boost::system::error_code& ec, std::size_t bytes_transferred)
	{
		// Check for errors.
		if (ec)
		{
			handler_(ec, bytes_transferred);
			return;
		}
		// read the status line
		std::istream ss(&streambuf_);
		std::getline(ss, request_.verb, ' ');
		std::getline(ss, request_.uri, ' ');
		char buf[5];
		ss.read(buf, 5);
		gethttpline(ss, request_.version);
		if (memcmp(buf, "HTTP/", 5))
		{
			handler_(boost::system::error_code(EILSEQ, boost::system::errno_ecat), bytes_transferred);
			return;
		}
		async_read_headers(stream_, streambuf_, request_.headers, handler_);
	}
//private:
	AsyncReadStream &stream_;
	Buffer &streambuf_;
	HTTP_Request &request_;
	ReadHandler handler_;
};

template <typename AsyncReadStream, typename Buffer, typename HTTP_Request, typename ReadHandler>
inline void* asio_handler_allocate(std::size_t size, async_read_request_handler<AsyncReadStream, Buffer, HTTP_Request, ReadHandler>* this_handler)
{
	return boost_asio_handler_alloc_helpers::allocate(size, &this_handler->handler_);
}

template <typename AsyncReadStream, typename Buffer, typename HTTP_Request, typename ReadHandler>
inline void asio_handler_deallocate(void* pointer, std::size_t size, async_read_request_handler<AsyncReadStream, Buffer, HTTP_Request, ReadHandler>* this_handler)
{
	boost_asio_handler_alloc_helpers::deallocate(pointer, size, &this_handler->handler_);
}

template <typename Function, typename AsyncReadStream, typename Buffer, typename HTTP_Request, typename ReadHandler>
inline void asio_handler_invoke(const Function& function, async_read_request_handler<AsyncReadStream, Buffer, HTTP_Request, ReadHandler>* this_handler)
{
	asio_handler_invoke_helpers::invoke(function, &this_handler->handler_);
}

} // namespace detail

template <typename Async_Read_Stream, typename Buffer, typename HTTP_Request, typename Handler>
void async_read_request(Async_Read_Stream &s, Buffer &b, HTTP_Request &r, Handler handler)
{
	// should be "\r\n"
	boost::asio::async_read_until(s, b, '\n', detail::async_read_request_handler<Async_Read_Stream, Buffer, HTTP_Request, Handler>(s, b, r, handler));
}

template <typename Buffer, typename HTTP_Request>
void write_request(Buffer &b, HTTP_Request const &r)
{
	std::ostream ss(&b);
	ss << r.verb << ' ' << r.uri << " HTTP/" << r.version << "\r\n";
	write_headers(b, r.headers);
}

template <typename S = std::string, typename H = std::map<S, S> >
struct basic_request
{
	typedef S string_type;
	typedef H headers_type;
	string_type verb;
	string_type uri;
	string_type version;
	headers_type headers;
};

typedef basic_request<> request;

}} // namespace alpha::http
